package ext.tianma.number.util;

import java.io.File;
import java.io.IOException;
import java.rmi.RemoteException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Vector;

import wt.change2.ChangeHelper2;
import wt.change2.Changeable2;
import wt.doc.WTDocument;
import wt.doc.WTDocumentMaster;
import wt.epm.EPMDocument;
import wt.epm.EPMDocumentMaster;
import wt.epm.structure.EPMReferenceLink;
import wt.epm.structure.EPMStructureHelper;
import wt.fc.Persistable;
import wt.fc.PersistenceHelper;
import wt.fc.QueryResult;
import wt.fc.ReferenceFactory;
import wt.fc.WTObject;
import wt.lifecycle.LifeCycleManaged;
import wt.org.WTPrincipal;
import wt.org.WTPrincipalReference;
import wt.part.WTPart;
import wt.part.WTPartConfigSpec;
import wt.part.WTPartHelper;
import wt.part.WTPartMaster;
import wt.part.WTPartReferenceLink;
import wt.part.WTPartStandardConfigSpec;
import wt.part.WTPartUsageLink;
import wt.pom.Transaction;
import wt.query.ClassAttribute;
import wt.query.OrderBy;
import wt.query.QuerySpec;
import wt.query.SearchCondition;
import wt.session.SessionHelper;
import wt.team.Team;
import wt.team.TeamManaged;
import wt.team.TeamReference;
import wt.util.WTContext;
import wt.util.WTException;
import wt.util.WTProperties;
import wt.util.WTPropertyVetoException;
import wt.vc.Iterated;
import wt.vc.VersionControlHelper;
import wt.vc.Versioned;
import wt.vc.config.ConfigHelper;
import wt.vc.config.ConfigSpec;
import wt.vc.config.LatestConfigSpec;
import wt.vc.struct.StructHelper;
import wt.vc.views.View;
import wt.vc.views.ViewHelper;
import wt.vc.views.ViewReference;
import wt.vc.wip.WorkInProgressHelper;
import wt.vc.wip.Workable;
import wt.workflow.engine.StandardWfEngineService;
import wt.workflow.engine.WfProcess;
import wt.workflow.engine.WfState;

import com.ptc.netmarkets.util.beans.NmURLFactoryBean;

import ext.com.iba.IBAUtil;

public class PartUtil{
	//private static final String RESOURCE = "ext.tianma.number.util.PartUtil";
	public static final String CURRENTSTATEATTR="CurrentState";
	private static boolean VERBOSE=false;	
	public static final String MPSTATESTR="批量生产阶段";
	public static int ai[] = {0};
	static{
		try{            
			VERBOSE= (WTProperties.getLocalProperties()).getProperty("ext.tianma.number.util.verbose", false);
		}
		catch(Throwable throwable){
			throw new ExceptionInInitializerError(throwable);
		}
	}
	/**
	 *输出调试信息
	 */
	private static void outDebugInfo(String info){
		if(VERBOSE)
			System.out.println(info);
	}	
	

	/** 
	 * @param     topPart	顶层件
	 * @param     batchReviewParts	随顶层件走流程的子件的vector
	 * @param     processStateStr  
	 * @exception wt.util.WTException
	 */		
	public static void setPartsState(WTPart topPart, Vector batchReviewParts,String processStateStr)throws WTException,Exception{
		Hashtable table=new Hashtable();
		table.put(CURRENTSTATEATTR, processStateStr);
		if(topPart!=null){
			IBAUtil.setSoftTypeIBAValues(topPart, table);			
		}
		if(batchReviewParts!=null && batchReviewParts.size()>0){
			for(int i=0; i<batchReviewParts.size(); i++){
				WTPart thisPart = (WTPart)batchReviewParts.elementAt(i);
				IBAUtil.setSoftTypeIBAValues(thisPart, table);				
			}
		}
		
	}
		
	/**
	 * 按照业务规则得到顶层件的子件和CAD文档，初始化变量batchReviewParts和batchReviewCADs
	 * @param     topPart	顶层件
	 * @param     batchReviewParts	随顶层件走流程的子件的vector
	 * @param     batchReviewCADs	batchReviewParts关联的CAD文档及其参考文档的vector
	 * @param     needChange	是否需要对非A版本的部件需要关联变更的判断
	 * @param     isPart  当前零部件是否是均胜汽车零部件的part
	 * @exception wt.util.WTException
	 */		
	public static void initVector(WTPart topPart, Vector batchReviewParts, Vector batchReviewCADs, Vector batchReviewDocs, boolean needChange,boolean isPart)throws WTException,Exception{
		//顶层件所在的库
		StringBuffer errBuf = new StringBuffer();
		Locale locale = WTContext.getContext().getLocale();
		//清空所有Vector
		if (batchReviewParts!=null && batchReviewParts.size()!=0)
			batchReviewParts.clear();
		if (batchReviewCADs!=null && batchReviewCADs.size()!=0)
			batchReviewCADs.clear();
		if (batchReviewDocs!=null && batchReviewDocs.size()!=0)
			batchReviewDocs.clear();								
		if(needChange){
			String topPartVersion = VersionControlHelper.getVersionIdentifier((Versioned)topPart).getValue();
			if(isPart){//如果是均胜汽车零部件的part
				if(!topPartVersion.endsWith("A")){
				//判断零部件当前的状态是什么
				//如果不是批量生产阶段，则需要关联工程更改通知单，否则必须是变更任务的最后所得项				
				String nowState = (String) IBAUtil.getIBAValue(topPart,CURRENTSTATEATTR);
				if(nowState!=null && !nowState.equalsIgnoreCase(MPSTATESTR)){
					Vector relateDoc = new Vector();
					String changeOrderState = "";	
					boolean hasValidChangeDoc = false;				       
					relateDoc = getRelatedDocListByPart(topPart,true,true);
					if(relateDoc!=null||relateDoc.size()>0){
						WTDocument doc = new  WTDocument();
						for(int i=0;i<relateDoc.size();i++){
						doc = (WTDocument)relateDoc.elementAt(i);
						//判断当前文档是否为变更单
						if(isChangeDoc(doc)){
						//判断变更单是否有效，即变更单的状态是否在正在工作和重新工作状态
						changeOrderState = doc.getLifeCycleState().toString();
						if(changeOrderState.equals("INWORK")||changeOrderState.equals("REWORK")){
							batchReviewDocs.addElement(doc);
							hasValidChangeDoc = true;
						}										
					    }
						}
					}						
					if(!hasValidChangeDoc && !isImplementedChangeable(topPart)){
						errBuf.append("\n" + "零部件("+ topPart.getIdentity() + ")没有关联到相关的工程更改通知单且不是变更任务的最后所得项，不能提交！");
					}			        
				}else{
					if(!isImplementedChangeable(topPart)){
						errBuf.append("\n" + "零部件("+ topPart.getIdentity() + ")不是变更任务的最后所得项！");
					}
					}
				}
			}
			else{//对象为博声的零部件			
				if(!topPartVersion.endsWith("A")){
					//updated by lauren on 2008/05/15：
					//如果零部件的上一个大版本的最新小版本为已确认，则必须关联改模通知书
					//获取零部件的上一大版本的最新小版本
					WTPart lastVersion = getPreVersionPart(topPart);
					String partState = "";
					if(lastVersion!=null){
						//partState = (new IBAUtil(lastVersion)).getIBAValue("BosenCurrentStatus");
						partState = (String)IBAUtil.getIBAValue(lastVersion,"BosenCurrentStatus");
					}
					//String partState = topPartIBA.getIBAValue("BosenCurrentStatus");
					//end2008/05/15
					if(partState.equalsIgnoreCase("已确认")){
						//modify by gaoxu 2008/03/05 modefied by lauren on 20090207
						//String partType = topPartIBA.getIBAValue("PartType");
						String partType = (String) IBAUtil.getIBAValue(topPart,"PartType");						
						System.out.println("顶层件的零部件类型="+partType);
						if(partType == null || partType.equals("")){
							 throw new WTException("顶层部件:"+topPart.getNumber()+"的零部件的类型不能为空,请更新!");
						}
						//end of						
						//如果为已确认的零件，必须关联一个已发布的确认类的改模通知书
						//否则必须是作为变更活动的最后所得项
						if(partType.equalsIgnoreCase("零件")){
							Vector relateDoc = new Vector();
							String changeOrderState = "";	
							boolean hasValidChangeDoc = false;					       
							relateDoc = getRelatedDocListByPart(topPart,true,true);
							if(relateDoc!=null||relateDoc.size()>0){
								WTDocument doc = new  WTDocument();
								for(int i=0;i<relateDoc.size();i++){
									doc = (WTDocument)relateDoc.elementAt(i);
									//判断当前文档是否为变更单
									if(isChangeModuleDoc(doc)){
										//判断变更单是否有效，即变更单的状态是否在正在工作和重新工作状态
										changeOrderState = doc.getLifeCycleState().toString();
										if(changeOrderState.equals("RELEASED")){
											hasValidChangeDoc = true;
										}										
									}
								}
							}
							
						if(!hasValidChangeDoc){
							errBuf.append("\n" + "零部件("+ topPart.getIdentity() + ")修订后没有关联到已经发布的改模通知书，不能提交！");
						}
						}
						else{
							if(!isImplementedChangeable(topPart)){
								errBuf.append("\n" + "成品/模块/组件零部件("+ topPart.getIdentity() + ")修订后不是变更任务的最后所得项！");
							}
						}						
					}					
				}
			}
		}	
		String viewname = "";
		ViewReference viewrf = topPart.getView();
		if(viewrf != null)
			viewname = viewrf.getName();
		Hashtable partsHt = new Hashtable(); //存放递归过的所有零部件，key为number，value为WTPart，防止重复查询
		
		//添加PBO及其CAD文档
		batchReviewParts.add(topPart);
		//获取主呼CAD文档及其参考文档
		getRelatedCADs(topPart,batchReviewCADs,errBuf);
		
		//获得子件,并过滤不同时走流程的子件
		//getReviewPartAndCAD(topPart, viewname, partsHt, batchReviewParts, batchReviewCADs,errBuf,needChange);
		getReviewPartAndCAD(topPart,viewname, partsHt, batchReviewParts, batchReviewCADs,batchReviewDocs, errBuf,needChange,isPart);		
		outDebugInfo("\t跟随流程走的成品 batchReviewParts.size() = "+batchReviewParts.size());	
		outDebugInfo("\t跟随流程走的CAD文档 batchReviewCADs.size() = "+batchReviewCADs.size());
		outDebugInfo("\t errBuf = "+errBuf);	
		if(errBuf.toString().length()>0){			
			throw new WTException(errBuf.toString());
		}
	}
	/*
	 *判断当前文档是否为变更单
	 *added by lauren on 2007-12-6
	 */
	public static boolean isChangeDoc(WTDocument module)throws WTException{
		String moduleType="";
		Locale locale= SessionHelper.manager.getLocale();
		if(module!=null){
			try{
				moduleType=wt.type.ClientTypedUtility.getExternalTypeIdentifier(module);
			}
			catch(RemoteException re){
				throw new WTException(re);
			}
		}			
		if((module!=null) && (moduleType.indexOf("ChangeNotifyDoc")>=0) ){
			return true;
		}
		return false;
	}
	

	/**
	 *判断部件是否作为某个变更任务的最后所得项
	 */	
	public static boolean isImplementedChangeable(WTPart part)throws WTException{
		Changeable2 changeable = (Changeable2)part;
		QueryResult activities = ChangeHelper2.service.getImplementedChangeActivities(changeable);                
		if(activities.hasMoreElements())
			return true;
		return false;
	}
		
	/**
	 * @param     module	wtdocument that need to judge the type
	 * @exception wt.util.WTException
	 * @throws RemoteException 
	 */	
	public static boolean isChangeModuleDoc(WTDocument module)throws WTException, RemoteException{
		String moduleType="";
		if(module!=null){
			try{
				moduleType=wt.type.ClientTypedUtility.getExternalTypeIdentifier(module);
				if (VERBOSE)
					System.out.println("(isChangeModuleDoc) "+module.getIdentity()+";moduleType ="+moduleType);
			}
			catch(RemoteException re){
				throw new WTException(re);
			}
		}
		if(module!=null && moduleType.indexOf("notifyDoc")>-1){
			//IBAUtil docIba = new IBAUtil(module);
			String subDocType = (String) IBAUtil.getIBAValue(module,"DocSubType");
			if(subDocType.equalsIgnoreCase("改模通知书"))
				return true;					
		}
		return false;
	}
		
	/**
	 *获取零部件关联的主呼CAD文档及其参考文档
	 */
	private static void getRelatedCADs(WTPart part, Vector batchReviewCADs, StringBuffer errBuf)throws WTException{
		Vector allCADs = new Vector();		
		allCADs = getAllRelatedCADListByPart(part);
		
		if(allCADs==null)
			return;		
		EPMDocument cad=null;
		for(int j=0;j<allCADs.size();j++){
			cad = (EPMDocument)allCADs.elementAt(j);	
			outDebugInfo("\t cad's identity = "+cad.getIdentity());			
			//是否检出
			if(WorkInProgressHelper.isCheckedOut(cad)){
				if(WorkInProgressHelper.isWorkingCopy((Workable)cad))
					errBuf.append("\n" + "CAD文档("+ cad.getIdentity() + ")处于检出状态！");
			}			
			//判断生命周期状态
			String state = cad.getLifeCycleState().toString();
			if(state.equals("INWORK")||state.equals("REWORK"))
				batchReviewCADs.add(cad);
		}		
	}
		
	 /**
	   * 根据零部件，获取其相关联的所有cad文件，包括相关CAD文档的参考文档
	   * @param part 想要获取相关cad文件的零部件
	   * @return Vector 返回该零部件的关联cad文件集合。如果零部件为空，返回空；如果零部件没有关联cad，返回空
	   * @throws WTException
	 **/
    public static Vector getAllRelatedCADListByPart(WTPart part)throws WTException{
    	Vector cadDocs = new Vector();
			if( part == null ){
				return cadDocs;
			}
			//获取part相关的所有文档，包括CAD文档和普通文档
			QueryResult qr = WTPartHelper.service.getDescribedByDocuments(part,true);//PartDocHelper.service.getAssociatedDocuments(part);
			while(qr.hasMoreElements()){
				Object wto = (Object)qr.nextElement();
				if(wto instanceof EPMDocument){
					EPMDocument epmdoc = (EPMDocument)wto;
					if (epmdoc != null){
						EPMDocument cad = getLatestEPMDocByMaster((EPMDocumentMaster)epmdoc.getMaster());
						cadDocs.add(cad);
						//获取CAD文档的参考文档，指挂在该CAD文档下的文档
						QuerySpec qs= new QuerySpec(EPMReferenceLink.class);
						//QueryResult qr1= EPMStructureHelper.service.navigateReferences(cad, qs, true);
						QueryResult qr1= EPMStructureHelper.service.navigateReferencedBy((EPMDocumentMaster)cad.getMaster(), qs, true);
						while(qr1.hasMoreElements()){
							Object obj = (Object)qr1.nextElement();
							if(obj instanceof EPMDocument){
								cadDocs.add( obj );
							}						
						}
					}
				}
			}
		return cadDocs;
    }
		    
	/**
	 * 根据EPM文档的master号获取最新版本的EPM文档
	 * @param master EPM文档的master
	 * @return EPMDocument 返回该master对应的EPM文档的最新版本。如果EPM文档不存在，返回空；如果master为空，返回空   
	 * @throws WTException	
	 **/ 
	public static EPMDocument getLatestEPMDocByMaster(EPMDocumentMaster master) 
		throws WTException {
		if(master == null){
			return null;
		}
		boolean flag = false;
		Iterated iter = null; 
		//获取该master对应的所有的epm对象
		QueryResult queryresult = VersionControlHelper.service.allIterationsOf(master);
		while(queryresult.hasMoreElements() && (!flag)){
			iter = (Iterated)(queryresult.nextElement());
			flag = iter.isLatestIteration(); 
		}
		return (EPMDocument)iter;
	}
		
	/**
	 * 获取随父件走流程的子件及其关联的CAD文档（以及参考文档） 
	 */
	private static void getReviewPartAndCAD(WTPart part, String viewname, Hashtable partsHt, Vector batchReviewParts, Vector batchReviewCADs,  Vector batchReviewDocs,StringBuffer errBuf,boolean needChange,boolean isPart)
		throws WTException, RemoteException{
		Vector uses = null;
		WTPart temp= (WTPart)partsHt.get(part.getNumber());
		if(temp==null){
			partsHt.put(part.getNumber(), part);
			uses = getSubPartsAndUsageLinks(part, viewname).getObjectVectorIfc().getVector();				
			//递归处理子件
			WTPartUsageLink link = null;
			for(int i=0; i<uses.size(); i++){
				Persistable[] element= (Persistable[])uses.elementAt(i);
				link = (WTPartUsageLink)element[0];
				if(element[1] instanceof WTPartMaster)
					continue;
				WTPart sub= (WTPart)element[1];
				//IBAUtil subIBA = new IBAUtil(sub);				    					
				//检查该部件当前能否提交，即检查是否有子件正在走流程，如果存在，则抛错，并提示给用户正在走流程的部件
				if(isRelatedRunningProcess(sub)){
					errBuf.append("\n" + "子件("+ sub.getIdentity() + ")关联有正在运行的流程！");
				}				
				//是否检出
				if(WorkInProgressHelper.isCheckedOut(sub)){
					errBuf.append("\n" + "子件("+ sub.getIdentity() + ")处于检出状态！");
				}					
				String state = sub.getLifeCycleState().toString();
				if(state.equals("INWORK")||state.equals("REWORK")){
					if(needChange){
						//如果是B版本及以上，检查零部件是否作为某个变更任务的最后所得项；如果没有则抛错
						/*String version = VersionControlHelper.getVersionIdentifier((Versioned)sub).getValue();
						if(!version.endsWith("A") && !PartProcessor.isImplementedChangeable(sub))
						{
							errBuf.append("\n" + "子件("+ sub.getIdentity() + ")不是变更任务的最后所得项！");
						}*/
					String version = VersionControlHelper.getVersionIdentifier((Versioned)sub).getValue();
					if(isPart){//如果是均胜汽车零部件的part
						if(!version.endsWith("A")){
							//判断零部件当前的状态是什么
							//如果不是批量生产阶段，则需要关联工程更改通知单，否则必须是变更任务的最后所得项							
							String nowState = (String) IBAUtil.getIBAValue(sub,CURRENTSTATEATTR);
							if(nowState!=null && !nowState.equalsIgnoreCase(MPSTATESTR)){
								Vector relateDoc = new Vector();
								String changeOrderState = "";	
								boolean hasValidChangeDoc = false;							       
								relateDoc = getRelatedDocListByPart(sub,true,true);
								if(relateDoc!=null||relateDoc.size()>0){
									WTDocument doc = new  WTDocument();
									for(int ii=0;ii<relateDoc.size();ii++){
										doc = (WTDocument)relateDoc.elementAt(ii);
										//判断当前文档是否为变更单
										if(isChangeDoc(doc)){
										//判断变更单是否有效，即变更单的状态是否在正在工作和重新工作状态
										changeOrderState = doc.getLifeCycleState().toString();
										if(changeOrderState.equals("INWORK")||changeOrderState.equals("REWORK")){
											if(!batchReviewDocs.contains(doc))
												batchReviewDocs.addElement(doc);
											hasValidChangeDoc = true;
										}										
									    }
									}
								}
									
							if(!hasValidChangeDoc && !isImplementedChangeable(sub))
							{
								errBuf.append("\n" + "零部件("+ sub.getIdentity() + ")没有关联到相关的工程更改通知单且不是某变更任务的最后所得项，不能提交！");
							}							        
						}else{
							if(!isImplementedChangeable(sub)){
								errBuf.append("\n" + "零部件("+ sub.getIdentity() + ")不是变更任务的最后所得项！");
							}
							}
		            }
              }else{
					if(!version.endsWith("A")){
						String partState = (String) IBAUtil.getIBAValue(sub,"BosenCurrentStatus");					
						if(partState.equalsIgnoreCase("已确认")){
							//add by gaoxu 2008/03/05 modeified by lauren on 20090207							
							String partType = (String) IBAUtil.getIBAValue(sub,"PartType");
							System.out.println("partType="+partType);
							if(partType == null || partType.equals("")){
								 throw new WTException("结构中的子件:"+sub.getNumber()+"的零部件类型不能为空,请更新!");
							}
							//end of
							//如果为已确认的零件，必须关联一个已发布的确认类的改模通知书
							//否则必须是作为最后所得项
							if(partType.equalsIgnoreCase("零件")){
								Vector relateDoc = new Vector();
								String changeOrderState = "";	
								boolean hasValidChangeDoc = false;						       
								relateDoc = getRelatedDocListByPart(sub,true,true);
								if(relateDoc!=null||relateDoc.size()>0){
									WTDocument doc = new  WTDocument();
									for(int ii=0;ii<relateDoc.size();ii++){
										doc = (WTDocument)relateDoc.elementAt(ii);
										//判断当前文档是否为变更单
										if(isChangeModuleDoc(doc)){
											//判断变更单是否有效，即变更单的状态是否在正在工作和重新工作状态
											changeOrderState = doc.getLifeCycleState().toString();
											if(changeOrderState.equals("RELEASED")){
												hasValidChangeDoc = true;
											}										
										}
									}
								}
								
								if(!hasValidChangeDoc){
									errBuf.append("\n" + "零部件("+ sub.getIdentity() + ")没有关联到相关的工程更改通知单，不能提交！");
								}
							}
							else{
								if(!isImplementedChangeable(sub)){
									errBuf.append("\n" + "零部件("+ sub.getIdentity() + ")不是变更任务的最后所得项！");
								}
							}							
						}						
					}
				}
			}
			batchReviewParts.add(sub);				
			//获取主呼CAD文档及其参考文档
			getRelatedCADs(sub,batchReviewCADs,errBuf);
		}				
		getReviewPartAndCAD(sub,viewname, partsHt, batchReviewParts, batchReviewCADs,batchReviewDocs, errBuf,needChange,isPart);
	}			
    }		
	}
  
	/**
	 *函数功能：判断当前的对象是不是关联正在运行的工作流
	 *输入参数：WTObject：obj 
	 *true:存在关联
	 */
	public static boolean isRelatedRunningProcess(WTObject obj)
		throws WTException
	{
		StandardWfEngineService standardwfengineservice = new StandardWfEngineService();
		Enumeration processes = null;
		//得到与obj关联的正在运行的工作流
		processes = standardwfengineservice.getAssociatedProcesses(obj, WfState.OPEN_RUNNING);
		if (processes.hasMoreElements())
			return true;
		else
			return false;
	}
			
	/**
	 * 设置所有零部件\CAD文档的状态
	 * @param	batchReviewParts	设置所有零部件
	 * @param	batchReviewCADs	设置所有零部件
	 * @param	nextStateStr	要设置的状态
	 * @exception wt.util.WTException
	 */		
	public static void setAllObjectsState(Vector batchReviewParts, Vector batchReviewCADs, String nextStateStr)
		throws WTException
	{
		Transaction transaction=null;
		try
		{					
			transaction= new Transaction();
			transaction.start();
			if(batchReviewParts != null && batchReviewParts.size() > 0)
			{				
				for(int i=0; i < batchReviewParts.size(); i++)
				{
					WTObject wto= (WTObject)batchReviewParts.elementAt(i);
					ext.com.workflow.WorkflowUtil.setLifeCycleState((LifeCycleManaged)wto,nextStateStr);
					outDebugInfo("\t"+wto.getIdentity()+" setLifeCycleState to "+nextStateStr);
					//如果零部件类型是零件,则设置当前状态为未确认 modify by gaox 2008/03/03
				/*	WTPart part=(WTPart)wto;
					IBAUtil ibaUti = new IBAUtil(part);
					String partType = ibaUti.getIBAValue("PartType");
					if(partType.equals("零件"))
					{
						IBAUtil.setIBAStringValue(part,"BosenCurrentStatus","未确认");
					}
					//end of*/	
				}
			}
			if(batchReviewCADs != null && batchReviewCADs.size() > 0)
			{				
				for(int i=0; i < batchReviewCADs.size(); i++)
				{
					WTObject wto= (WTObject)batchReviewCADs.elementAt(i);
					ext.com.workflow.WorkflowUtil.setLifeCycleState((LifeCycleManaged)wto,nextStateStr);
					outDebugInfo("\t"+wto.getIdentity()+" setLifeCycleState to "+nextStateStr);
				}
			}			
			transaction.commit();
			transaction=null;
		}
		catch(WTException e)
		{
			e.printStackTrace();
		}
		finally
		{
			if(transaction != null)
				transaction.rollback();
		}
	}
	
	/**
	 *流程签审指南链接
	 */	
	public static String getWFTipForPartLink(WTObject processObj,String myCfgFilePath)
		throws WTException
	{
		if (processObj == null) return "";
		outDebugInfo(">>>enter PartUtil.getWFTipForPartLink()...");
		Locale locale = WTContext.getContext().getLocale();					
	    //String display= WTMessage.getLocalizedMessage(RESOURCE, "partGuide", null, locale);	//"零部件流程签审指南";
		String urlLink = "";
		try
		{		
			myCfgFilePath = myCfgFilePath.substring(myCfgFilePath.indexOf("ext"+File.separator+"generic"+File.separator+"cfg"+File.separator));
			outDebugInfo("\t myCfgFilePath = "+myCfgFilePath);	
			myCfgFilePath = myCfgFilePath.replace(File.separatorChar,'/');		
			outDebugInfo("\t myCfgFilePath 2 = "+myCfgFilePath);	
			NmURLFactoryBean urlFactoryBean = new NmURLFactoryBean();
			urlLink = urlFactoryBean.getFactory().getBaseURL().toString();
			urlLink = urlLink + myCfgFilePath;
			outDebugInfo("\t urlLink = "+urlLink);	
			//urlLink = "<a href="+ urlLink + ">" + display + "</a>";
		}
		catch (Exception e)
		{
			System.out.println("PartUtil.getWFTipForPartLink() : error");
			e.printStackTrace();
		}
		outDebugInfo("<<<out PartUtil.getWFTipForPartLink()--" + urlLink);
		return urlLink;		
	}
	
	/**
	 *intialize the roles of the processObj by the recent process instance which template equal processTemplateName and created by same creator of processObj
	 */
	public static void initializeRoleHolderByProcessCreator(Object currentProcessObj,String processTemplateName)throws WTException,IOException{
		outDebugInfo(">>>>>>>>>>.initializeRoleHolderByProcessCreator()");				
		WfProcess currentProcess= ext.com.workflow.WorkflowUtil.getProcess(currentProcessObj);
		WTPrincipal curCreator=(WTPrincipal)((WTPrincipalReference)currentProcess.getCreator()).getObject();				
		Team curTeam = (Team)((TeamReference)((TeamManaged)currentProcess).getTeamId()).getObject();
		curTeam= (Team)PersistenceHelper.manager.refresh(curTeam);
		Vector curRoles = curTeam.getRoles();
		outDebugInfo("    the roles of currentProcess=" + curRoles);
		if((curRoles==null) || (curRoles.size()<=0))
			return;
		//remove the system roles
		for(int i=0; i< curRoles.size(); i++){
			Object role = curRoles.elementAt(i);
			if(role.toString().equals("PROMOTER") || role.toString().equals("SUBMITTER")){
				curRoles.removeElementAt(i);
				i--;
			}
		}				
		if((curRoles==null) || (curRoles.size()<=0)){
			outDebugInfo("	remained roles=null, exit!!!");
			return;
		}
		//clear the role's participates
		for(int i=0; i< curRoles.size(); i++){
			Object role = curRoles.elementAt(i);
			java.util.Enumeration enums = curTeam.getPrincipalTarget(wt.project.Role.toRole(role.toString()));
			outDebugInfo("clear curRole is: "  + role);
			while(enums.hasMoreElements()){
				wt.org.WTPrincipalReference princ = (wt.org.WTPrincipalReference)(enums.nextElement());
				outDebugInfo("delete cur Team principal is: "  + ((WTPrincipal)(princ.getObject())).getName());
				curTeam.deletePrincipalTarget(wt.project.Role.toRole(role.toString()), (wt.org.WTPrincipal)princ.getObject());
			}					
		}
		WTProperties wtproperties = WTProperties.getLocalProperties();
		
		//search recently wfprocess's range,get it from site.conf
		int searchRange=wtproperties.getProperty("ext.tianma.util.wfsearchrange",30);				
		outDebugInfo("Process searchRange="+searchRange);				
		boolean needUpdate=false;
		ReferenceFactory rf= new ReferenceFactory();
		String curProcessOid= rf.getReferenceString(currentProcess);
		QueryResult enuHistoryProcess=getRecentProcess(processTemplateName,curCreator,searchRange,null);				
		while(enuHistoryProcess.hasMoreElements()){
			WfProcess historyProcess= (WfProcess)enuHistoryProcess.nextElement();
			outDebugInfo("get history recently process="+ historyProcess);
			if( curProcessOid.equals(rf.getReferenceString(historyProcess)))
				continue;
			if(historyProcess instanceof wt.team.TeamManaged){				
				TeamReference tf =(TeamReference)((TeamManaged)historyProcess).getTeamId();
				if (tf ==null)
					continue;						
				Team historyTeam =null;
				try{
					historyTeam = (Team)(tf.getObject());	
				}
				catch (wt.util.WTRuntimeException e){
					System.out.println("error ="+e.toString());
					continue;
				}																		
				if (historyTeam ==null)
					continue;						
				Vector historyRoles =historyTeam.getRoles();
				outDebugInfo("    historyProcess(" + historyProcess.getIdentity() + ") team roles=" + historyRoles);
				for(int i=0; i<curRoles.size(); i++){
					String curRoleName=curRoles.elementAt(i).toString();
					Object curRole = curRoles.elementAt(i);
					java.util.Enumeration enums = historyTeam.getPrincipalTarget(wt.project.Role.toRole(curRole.toString()));
					while(enums.hasMoreElements()){
						wt.org.WTPrincipalReference princ = (wt.org.WTPrincipalReference)(enums.nextElement());
						if(VERBOSE)
							System.out.println("add "+((WTPrincipal)princ.getObject()).getName()+" to curTeam " + curTeam.getName());
						curTeam.addPrincipal(wt.project.Role.toRole(curRole.toString()), (WTPrincipal)princ.getObject());
						needUpdate = true;
					}							
				}
				outDebugInfo("    needUpdate="+needUpdate );
				if(needUpdate)
					break;
			}
		}
		outDebugInfo("<<<<<<<<<<<.initializeRoleHolderByProcessCreator()");
	}
	
	/**
	 * 根据流程模板名称、启动者、启动时间、运行状态（已执行）等条件搜索符合条件的流程
	 */
	public static QueryResult getRecentProcess(String processTemplateName,WTPrincipal creator, int timeRange, Locale locale) throws WTException
	{
		if(VERBOSE)
			System.out.println("    >>>getRecentProcess()--processs templat name=" + processTemplateName);
		
		if(creator==null)
		{
			creator= SessionHelper.manager.getPrincipal();
		}
		if(locale==null)
			locale= SessionHelper.manager.getLocale();
		if(VERBOSE)
			System.out.println("    creator=" + creator.getName());
		Calendar calendar= Calendar.getInstance(locale);
		calendar.add(Calendar.DATE, -(timeRange));
		Timestamp timeStamp= new Timestamp(calendar.getTime().getTime());
		
		QuerySpec querysearch=null;
		QueryResult queryresult=null;
		boolean hasCondition=false;
		querysearch= new QuerySpec(WfProcess.class);
		if(processTemplateName!=null && processTemplateName.length()>0)
		{
			SearchCondition sc5= new SearchCondition(WfProcess.class, "name", SearchCondition.LIKE, "%"+processTemplateName+"%");
			querysearch.appendWhere(sc5);
			hasCondition=true;
		}
		
		SearchCondition sc6= new SearchCondition(WfProcess.class, "creator.key", SearchCondition.LIKE, PersistenceHelper.getObjectIdentifier(creator));
		if(hasCondition)
		{
			querysearch.appendAnd();
		}
		querysearch.appendSearchCondition(sc6);
		
		SearchCondition sc7 = new SearchCondition(WfProcess.class, "thePersistInfo.createStamp", SearchCondition.GREATER_THAN, timeStamp);
		querysearch.appendAnd();
		querysearch.appendSearchCondition(sc7);
		
		SearchCondition sc8 = new SearchCondition(WfProcess.class, "state", SearchCondition.LIKE, WfState.CLOSED_COMPLETED_EXECUTED);
		querysearch.appendAnd();
		querysearch.appendSearchCondition(sc8);
		
		OrderBy orderby = new OrderBy(new ClassAttribute(WfProcess.class, "thePersistInfo.createStamp"), true);	//true为降序，false为升序
		querysearch.appendOrderBy(orderby, ai);
		
		queryresult= PersistenceHelper.manager.find(querysearch);		
		return queryresult;
	}
	
	/**
	 * 根据零部件，获取其相关联的普通文档（类型为WTDocument）；根据参数控制获取的是描述文档还是参考文档
	 * @param part	 想要获取相关文档的零部件
	 * @param discribeDoc 是否要获取part的描述文档
	 * @param referenceDoc 是否要获取part的参考文档
	 * @return 返回该零部件的相关文档列表。如果零部件为空，返回空；如果没有相关文档，返回空
	 */
	public static Vector getRelatedDocListByPart(WTPart part, boolean discribeDoc, boolean referenceDoc)
		throws WTException{
		if(part==null)
			return null;
		
		Vector docV = new Vector();		
		if(discribeDoc == true)
		{
			// 得到描述文档
			QueryResult queryresult= WTPartHelper.service.getDescribedByWTDocuments(part);     
			while(queryresult!= null && queryresult.hasMoreElements())
			{
				WTObject obj= (WTObject)queryresult.nextElement();
				if(obj instanceof WTDocument)               
					docV.addElement(obj); 
				
			}
		}
		if(referenceDoc == true)
		{			
			//得到part的参考文档
			QueryResult qr= PersistenceHelper.manager.navigate(part, "references", WTPartReferenceLink.class, false);        
			while(qr !=null && qr.hasMoreElements())
			{
				//得到link
				WTPartReferenceLink link = (WTPartReferenceLink)qr.nextElement();
				
				//得到link的docmaster
				WTDocumentMaster docmaster =(WTDocumentMaster)link.getReferences();
				
				//根据master得到doc
				WTDocument doc=DocUtil.getLatestWTDocument(docmaster);
				docV.addElement(doc);
			}
		}
		return docV;
	}	
	/**
	 * 根据零部件，获取其上一大版本的最新小版本
	 * @param part 要获取上一大版本的最新小版本的零部件
	 * @return 返回上一版本的最新小版本的零部件，如果零部件本身为A版本，返回空
	 */
	public static WTPart getPreVersionPart(WTPart part)
		throws WTException
	{
		WTPart prevVersionPart=null;
		// 得到零部件的小版本
		String curVersion = VersionControlHelper.getIterationIdentifier((Iterated)part).getValue();
		// 得到零部件得大版本
		String Version=VersionControlHelper.getVersionIdentifier((Versioned)part).getValue();
		// WTPart prepart=null;
		boolean notGet=true;
		if(Version.equals("A"))
			return null;
		try
		{  
			//得到所有零部件的所有大版本的最新小版本
			QueryResult allIterations=VersionControlHelper.service.allVersionsFrom((Versioned)part);
			if(allIterations!=null)
				while(allIterations.hasMoreElements()&&notGet)
				{    
					prevVersionPart=(WTPart)allIterations.nextElement();
					String theVersion=VersionControlHelper.getVersionIdentifier((Versioned)prevVersionPart).getValue();
					//如果是当前的大版本，跳出
					if(theVersion.equalsIgnoreCase(Version))
						continue;
					else
					{
						notGet=false;
					}
				}
		}
		catch (WTException wte)
		{
			wte.printStackTrace();
		}
		return prevVersionPart;
	}
	
	/**
	 * 根据某一特定视图同时获取传入零部件的所有一级子件及其usagelinks 
	 * @param part 传入零部件
	 * @param view 零部件的视图（允许为空视图） 
	 * @return QueryResult 
	 * @throws WTException
	 */
	public static QueryResult getSubPartsAndUsageLinks(WTPart part,String view)
		throws WTException
	{
		//得到视图的配置规范
		View viewObj = null; 
		if(view != null && view.length() > 0)
        	viewObj = ViewHelper.service.getView(view);
        WTPartConfigSpec config = WTPartHelper.service.findWTPartConfigSpec();
        WTPartStandardConfigSpec standardConfig= config.getStandard();
        try
        {
            standardConfig.setView(viewObj);
        }
        catch(WTPropertyVetoException wpve)
        {
            throw new WTException(wpve);
        }
        QueryResult qr = WTPartHelper.service.getUsesWTParts(part,standardConfig);
        return qr;                
	}
	
	/**
	 * 根据某一特定视图获取当前传入零部件的所有子件列表(不包括零部件本身)，返回Vector类型列表
	 * @param part 传入零部件
	 * @param view 零部件的视图（允许为空视图） 
	 * @return 返回该零部件的所有子件（WTPart）的列表
	 * @throws WTException
	 */
	public static Vector getSubPartListByPart(WTPart part,String view)
		throws WTException
	{
		Hashtable allHash = new Hashtable();
		allHash = getSubPartListByPart(part,view,allHash);	
		Collection collection = allHash.values();
		return new Vector(collection);			
	}
	
	/**
	 * 根据某一特定视图获取当前传入零部件的所有子件列表(不包括零部件本身)，返回Hashtable类型列表
	 * @param part 传入零部件
	 * @param view 零部件的视图（允许为空视图） 
	 * @param allHash 所有子件列表，其中key为子件编号，value为子件本身	
	 * @return Hashtable allHash
	 * @throws WTException
	 */	
	public static Hashtable getSubPartListByPart(WTPart part,String view,Hashtable allHash)
		throws WTException
	{
		//得到零部件的第一层子件
		Vector v = getFirstLevelSubPartListByPart(part,view);
		//遍历子件
		for(int i=0; i<v.size();i++)
		{
			WTPart subPart = (WTPart)v.elementAt(i);
			allHash.put(subPart.getNumber(),subPart);
			// 递规查询
			allHash=getSubPartListByPart(subPart,view,allHash);
		}
		return allHash;
	}
	
	/**
	 * 根据零部件，获取零部件的第一层子件的列表，列表中不包括零部件本身。
	 * @param part 想要获取子件的零部件 view所用视图规范
	 * @return 返回该零部件的第一层子件（WTPart）的列表。如果部件为空，返回空；如果子件为空，返回空	
	 */
	public static Vector getFirstLevelSubPartListByPart(WTPart part,String view)
		throws WTException
	{
		Vector partList = new Vector();
		//如果使用视图查询产品结构，可修改此行代码，换为视图配置规范；
		ConfigSpec latestconfigspec = new LatestConfigSpec();  
		if(view != null && view.length()>0)
		{
			View viewObj= ViewHelper.service.getView(view);
			WTPartConfigSpec config = WTPartHelper.service.findWTPartConfigSpec();
			WTPartStandardConfigSpec standardConfig= config.getStandard();
			try
			{
				standardConfig.setView(viewObj);
			}
			catch(WTPropertyVetoException wpve)
			{
				throw new WTException(wpve);
			}
			latestconfigspec = standardConfig;
		}
		QueryResult qr= StructHelper.service.navigateUsesToIteration(part,WTPartUsageLink.class,false,latestconfigspec);
		while(qr != null && qr.hasMoreElements())
		{
			//每一个element实际是一个persistable数组
			Persistable apersistable[] = (Persistable[])qr.nextElement();  
			//数组中第一个对象是usagelink
			WTPartUsageLink partLink= (WTPartUsageLink)apersistable[0];   
			//数组中第二个对象是子件（如果当前用户的权限正常，应该是个WTPart，反之返回的是WTPartMaster，一般是前一种情况居多，这样可以省掉查询子件最新版本的代码，这样代码的执行效率大大增加）
			Object uses= apersistable[1];   
			WTPart subPart=null; 
			if(uses instanceof WTPartMaster)
			{
				subPart = getLatestPartByPartMaster((WTPartMaster)uses);
			}
			else
				subPart=(WTPart)uses; 
			if(subPart != null && !isPersistableInobjVector(partList,subPart))
				partList.add(subPart);
		}
		return partList;
	}
	
	/**
	 * 根据零部件的master，获取零部件的最新小版本
	 * @param partMaster 想要获取最新小版本的零部件的master
	 * @return 返回该master对应的最新小版本的零部件，如果master为空，返回空
	 */
	public static WTPart getLatestPartByPartMaster(WTPartMaster partMaster)
		throws WTException{
		Iterated iter=null; 
		boolean flag=false;
		LatestConfigSpec latestconfigspec = new LatestConfigSpec();
		//根据零部件的master和latestconfigspec得到该master的所有小版本
		QueryResult queryresult = ConfigHelper.service.filteredIterationsOf(partMaster, latestconfigspec);
		if(queryresult !=null && queryresult.size()<=0)
		{
			ConfigSpec configspec = ConfigHelper.service.getDefaultConfigSpecFor(WTPartMaster.class);
			queryresult = ConfigHelper.service.filteredIterationsOf(partMaster, configspec);
		}
		
		while( queryresult.hasMoreElements() && (!flag) )
		{ 
			iter = (Iterated)(queryresult.nextElement());
			flag = iter.isLatestIteration();
		}
		if( iter != null)
			System.out.println("    the latest iteration=" + iter.getIdentity());
		return (WTPart)iter;
	}
	
	/**
	 * 检查某一个可持续对象是否在列表中
	 * @param objVector 检查的列表
	 * @param destination 想要检查的对象
	 * @return 如果对象在列表中，返回true，否则，返回false  
	 * @throws WTException
	 */
	public static boolean isPersistableInobjVector(Vector objVector, Persistable destination) 
		throws WTException{
		if((objVector==null) || (objVector.size()<=0))
			return false;
		if(objVector.contains(destination))
		{
			return true;
		}
		boolean flag= false;
		for(int i=0; i<objVector.size(); i++)
		{
			flag= PersistenceHelper.isEquivalent((Persistable)objVector.elementAt(i), destination);
			if(flag)
				return flag;
		}
		return flag;
	}
	
	/**
	 * 根据零部件编号获取最新版本的零部件
	 * @param partNumber 零部件编号
	 * @return 返回该编号对应的零部件的最新版本。如果零部件不存在，返回空；如果零部件编号为空，返回空
	 * @throws WTException
	 * <br><br>
	 * <b>Revision History</b>
	 * <br><b>Rev:</b> 1.0 - 2006/03/15, Gaoxu
	 * <br><b>Comment:</b> Initial release.  
	 */
	public static WTPart getWTPartByNumber(String partNumber) 
		throws WTException
	{
		WTPart part=null;
		QuerySpec qs=null;
		QueryResult qr=null;
		qs= new QuerySpec(WTPartMaster.class);
		//根据零件编号查询
		SearchCondition sc = new SearchCondition(WTPartMaster.class, "number", SearchCondition.LIKE, partNumber);		     
		qs.appendSearchCondition(sc);		    			
		qr= PersistenceHelper.manager.find(qs);
		while(qr.hasMoreElements())
		{
			WTPartMaster master=(WTPartMaster)qr.nextElement();	
			//根据master得到最新版本的零件
			part= getLatestPartByPartMaster(master);  
			break;
		}
		return part;
	}
}